
#include "auth-lib.h"
#include <jni.h>
#include "MD5.h"
#include <string>

using namespace std;

#define BYTE unsigned char
extern "C" JNIEXPORT jstring JNICALL
Java_com_dtalks_auth_AuthNative_businessKey(JNIEnv *env, jclass, jobject m_context, jstring code) {


    //    const char *originStr = env->GetStringUTFChars((jstring)DeviceID, (jboolean*)false);
//    MD5 md5 = MD5(originStr);
//    std::string md5Result = md5.hexdigest();

    if (m_context == nullptr) {
        return env->NewStringUTF("Error: Context is null");
    }
    if (code == nullptr) {
        return env->NewStringUTF("Error: Code is null");
    }
    jstring device_id = deviceId(env, m_context);
    if (device_id == nullptr){
        device_id = code;
    }
    jstring device_mac = deviceMac(env);
    if (device_mac == nullptr){
        device_mac = code;
    }
    char const *codeKey = env->GetStringUTFChars(code, nullptr);
    char const *deviceId = env->GetStringUTFChars(device_id, nullptr);
    char const *deviceMac = env->GetStringUTFChars(device_mac, nullptr);

    std::string const &device = std::string(deviceId) + std::string(deviceMac);
    std::string const &cc = std::string(device) + std::string(codeKey);

    time_t t = time(nullptr);
    char time[64] = {0};
    strftime(time, sizeof(time) - 1, "%Y%m%d%H", localtime(&t));

    std::string const &ccc = std::string(cc) + std::string(time);

    MD5 md5 = MD5(ccc);
    std::string md5Result = md5.hexdigest();

    //将char *类型转化成jstring返回给Java层
    return env->NewStringUTF(md5Result.c_str());
}


extern "C" JNIEXPORT jstring JNICALL
Java_com_dtalks_auth_AuthNative_deviceKey(JNIEnv *env, jclass, jobject m_context, jobject usbDevice,
                                          jstring code) {

    if (m_context == nullptr) {
        return env->NewStringUTF("Error: Context is null");
    }
    if (code == nullptr) {
        return env->NewStringUTF("Error: Code is null");
    }
    if (usbDevice == nullptr) {
        return env->NewStringUTF("Error: UsbDevice is null");
    }
    jclass device_class = env->FindClass("android/hardware/usb/UsbDevice");
    jfieldID bean_product_name = env->GetFieldID(device_class, "mProductName",
                                                 "Ljava/lang/String;");
    if (bean_product_name == nullptr) {
        return env->NewStringUTF("Error: u_0");
    }
    auto product_name = env->GetObjectField(usbDevice, bean_product_name);
    if (product_name == nullptr) {
        return env->NewStringUTF("Error: u_1");
    }
    std::string m_key = "Xmodule";
    std::string p_key = getString(env, (jstring) product_name);
    string::size_type idx;
    idx = p_key.find(m_key);
    if (idx == string::npos) {
        return env->NewStringUTF("Error: Unauthorized devices");
    }

    jstring device_id = deviceId(env, m_context);
    if (device_id == nullptr){
        device_id = code;
    }
    jstring device_mac = deviceMac(env);
    if (device_mac == nullptr){
        device_mac = code;
    }
    char const *codeKey = env->GetStringUTFChars(code, nullptr);
    char const *deviceId = env->GetStringUTFChars(device_id, nullptr);
    char const *deviceMac = env->GetStringUTFChars(device_mac, nullptr);
    std::string const &device = std::string(deviceId) + std::string(deviceMac);
    std::string const &cc = std::string(device) + std::string(codeKey);

    MD5 md5 = MD5(cc);
    std::string md5Result = md5.hexdigest();
    return env->NewStringUTF(md5Result.c_str());
}

std::string getString(JNIEnv *env, jstring j_string) {
    const char *path;
    path = env->GetStringUTFChars(j_string, nullptr);
    std::string spFn(path);
    env->ReleaseStringUTFChars(j_string, path);
    return spFn;
}


jstring deviceId(JNIEnv *env, jobject m_context) {
//    if (m_context == nullptr) {
//        return env->NewStringUTF("Error: Context is null");
//    }
    jclass cls_context = env->FindClass("android/content/Context");
    if (cls_context == nullptr) {
        return env->NewStringUTF("Error: d_0");
    }

    jmethodID getSystemService = (env)->GetMethodID(cls_context, "getSystemService",
                                                    "(Ljava/lang/String;)Ljava/lang/Object;");
    if (getSystemService == nullptr) {
        return env->NewStringUTF("Error: d_1");
    }

    jfieldID TELEPHONY_SERVICE = (env)->GetStaticFieldID(cls_context, "TELEPHONY_SERVICE",
                                                         "Ljava/lang/String;");
    if (TELEPHONY_SERVICE == nullptr) {
        return env->NewStringUTF("Error: d_2");
    }

    auto telephony_str = (jstring) (env)->GetStaticObjectField(cls_context, TELEPHONY_SERVICE);
    jobject telephony_manager = ((env)->CallObjectMethod(m_context, getSystemService,
                                                         telephony_str));
    if (telephony_manager == nullptr) {
        return env->NewStringUTF("Error: d_3");
    }

    jclass cls_TelephoneManager = (env)->FindClass("android/telephony/TelephonyManager");
    if (cls_TelephoneManager == nullptr) {
        return env->NewStringUTF("Error: d_4");
    }

    jmethodID getDeviceId = ((env)->GetMethodID(cls_TelephoneManager, "getDeviceId",
                                                "()Ljava/lang/String;"));
    if (getDeviceId == nullptr) {
        return env->NewStringUTF("Error: d_5");
    }

    jobject DeviceID = (env)->CallObjectMethod(telephony_manager, getDeviceId);
    return (jstring) DeviceID;
}

jstring deviceMac(JNIEnv *env) {
    //通过JNI找到java中的NetworkInterface类
    jclass cls_networkInterface = env->FindClass("java/net/NetworkInterface");
    if (cls_networkInterface == nullptr) {
        return env->NewStringUTF("");
    }
//找到getByName方法
    jmethodID j_method_id_1 = env->GetStaticMethodID(cls_networkInterface, "getByName",
                                                     "(Ljava/lang/String;)Ljava/net/NetworkInterface;");
    if (j_method_id_1 == nullptr)
        return env->NewStringUTF("Error: m_0");
    std::string ss = "wlan0";
    jstring jss2 = env->NewStringUTF(ss.c_str());
//调用getByname方法返回NetworkInterface的实例
    jobject j_object_1 = env->CallStaticObjectMethod(cls_networkInterface, j_method_id_1, jss2);
//找到getHardAddress方法
    jmethodID getHardwareAddress = env->GetMethodID(cls_networkInterface, "getHardwareAddress",
                                                    "()[B");
    if (getHardwareAddress == nullptr)
        return env->NewStringUTF("Error: m_1");
//调用getHardAddress方法获取MAC地址的byte[]数组
    auto j_byte_1 = (jbyteArray) env->CallObjectMethod(j_object_1, getHardwareAddress);
//下面一些列流程就是讲byte[]数组转换成char类型字符在转换成字符串
    auto *old_data = (jbyte *) env->GetByteArrayElements(j_byte_1, nullptr);
    jsize old_size = env->GetArrayLength(j_byte_1);
// BYTE定义为  #define BYTE unsigned char
    BYTE *byte_arr = (BYTE *) old_data;
    int len = (int) old_size;

    char *data = (char *) env->GetByteArrayElements(j_byte_1, nullptr);
    char *temp = new char[len * 2 + 1];
    memset(temp, 0, len * 2 + 1);
    for (int i = 0; i < len; i++) {
        char *buffer = new char[2];
        memset(buffer, 2, 0);
        sprintf(buffer, "%02X", data[i]);
        memcpy(temp + i * 2, buffer, 2);
        delete[] (buffer);
    }
    jstring jMac = char_to_string(env, temp);
    delete[] temp;
    return jMac;
}

jstring char_to_string(JNIEnv *env, const char *pat) {
    //定义java String类 strClass
    jclass strClass = (env)->FindClass("java/lang/String");
    //获取String(byte[],String)的构造器,用于将本地byte[]数组转换为一个新String
    jmethodID ctorID = (env)->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
    //建立byte数组
    jbyteArray bytes = (env)->NewByteArray(strlen(pat));
    //将char* 转换为byte数组
    (env)->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte *) pat);
    // 设置String, 保存语言类型,用于byte数组转换至String时的参数
    jstring encoding = (env)->NewStringUTF("GB2312");
    //将byte数组转换为java String,并输出
    return (jstring) (env)->NewObject(strClass, ctorID, bytes, encoding);
}

//
//extern "C" JNIEXPORT jstring JNICALL
//Java_com_dtalks_auth_AuthNative_usbDevice(JNIEnv *env, jclass, jobject object) {
//
////    jclass bean_clz = env->FindClass("com/jni/sample/JniBean");
////    jfieldID bean_name = env->GetFieldID(bean_clz, "name", "Ljava/lang/String;");
////    jfieldID bean_what = env->GetFieldID(bean_clz, "what", "I");
////    auto name = env->GetObjectField(object, bean_name);
////    jint what = env->GetIntField(object, bean_what);
////
////    const char * msg=env->GetStringUTFChars((jstring)name,nullptr);
////    char *str=new char[1024];
////    sprintf(str,"name:%s;what:%d(From C++)",msg,what);
////    jstring rs=env->NewStringUTF(str);
////    return rs;
//
//    jclass device_class = env->FindClass("android/hardware/usb/UsbDevice");
//    jfieldID bean_product_name = env->GetFieldID(device_class, "mProductName", "Ljava/lang/String;");
//    auto productName = env->GetObjectField(object, bean_product_name);
//    return (jstring)productName;
//}


//jstring jniGetHashMapInfo(JNIEnv *env, jobject object, jobject hashMapInfo) {
//    // 用于拼接字符串的数组
//    char buff[100] = {0};
//    // 用于拼接字符串的“游标”指针
//    char *pos = buff;
//
//    if (hashMapInfo == nullptr)
//        return env->NewStringUTF(buff);
//
//    // 获取HashMap类entrySet()方法ID
//    jclass hashmapClass = env->FindClass("java/util/HashMap");
//    jmethodID entrySetMID = env->GetMethodID(hashmapClass, "entrySet", "()Ljava/util/Set;");
//    // 调用entrySet()方法获取Set对象
//    jobject setObj = env->CallObjectMethod(hashMapInfo, entrySetMID);
//    // 调用size()方法获取HashMap键值对数量
////  jmethodID sizeMID = env->GetMethodID(hashmapClass, "size", "()I");
////  jint size = env->CallIntMethod(hashMapInfo, sizeMID);
//
//    // 获取Set类中iterator()方法ID
//    jclass setClass = env->FindClass("java/util/Set");
//    jmethodID iteratorMID = env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;");
//    // 调用iterator()方法获取Iterator对象
//    jobject iteratorObj = env->CallObjectMethod(setObj, iteratorMID);
//
//    // 获取Iterator类中hasNext()方法ID
//    // 用于while循环判断HashMap中是否还有数据
//    jclass iteratorClass = env->FindClass("java/util/Iterator");
//    jmethodID hasNextMID = env->GetMethodID(iteratorClass, "hasNext", "()Z");
//    // 获取Iterator类中next()方法ID
//    // 用于读取HashMap中的每一条数据
//    jmethodID nextMID = env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;");
//
//    // 获取Map.Entry类中getKey()和getValue()的方法ID
//    // 用于读取“课程-分数”键值对，注意：内部类使用$符号表示
//    jclass entryClass = env->FindClass("java/util/Map$Entry");
//    jmethodID getKeyMID = env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;");
//    jmethodID getValueMID = env->GetMethodID(entryClass, "getValue", "()Ljava/lang/Object;");
//
//    // HashMap只能存放引用数据类型，不能存放int等基本数据类型
//    // 使用Integer类的intValue()方法获取int数据
//    jclass integerClass = env->FindClass("java/lang/Integer");
//    jmethodID valueMID = env->GetMethodID(integerClass, "intValue", "()I");
//
//    // 循环检测HashMap中是否还有数据
//    while (env->CallBooleanMethod(iteratorObj, hasNextMID)) {
//        // 读取一条数据
//        jobject entryObj = env->CallObjectMethod(iteratorObj, nextMID);
//
//        // 提取数据中key值：String类型课程名字
//        auto courseJS = (jstring) env->CallObjectMethod(entryObj, getKeyMID);
//        if (courseJS == nullptr)   // HashMap允许null类型
//            continue;
//        // jstring转C风格字符串
//        const char *courseStr = env->GetStringUTFChars(courseJS, nullptr);
//
//        // 提取数据中value值：Integer类型分数，并转为int类型
//        jobject scoreObj = env->CallObjectMethod(entryObj, getValueMID);
//        if (scoreObj == nullptr)
//            continue;
//        int score = (int) env->CallIntMethod(scoreObj, valueMID);
//
//        // 拼接字符串，sprintf函数返回拼接字符个数
//        int strLen = sprintf(pos, "%s: ", courseStr);
//        pos += strLen;
//        int numLen = sprintf(pos, "%d. ", score);
//        pos += numLen;
//
//        // 释放UTF字符串资源
//        env->ReleaseStringUTFChars(courseJS, courseStr);
//        // 释放JNI局部引用资源
//        env->DeleteLocalRef(entryObj);
//        env->DeleteLocalRef(courseJS);
//        env->DeleteLocalRef(scoreObj);
//    }
//
//    // 释放JNI局部引用: jclass jobject
//    env->DeleteLocalRef(hashmapClass);
//    env->DeleteLocalRef(setObj);
//    env->DeleteLocalRef(setClass);
//    env->DeleteLocalRef(iteratorObj);
//    env->DeleteLocalRef(iteratorClass);
//    env->DeleteLocalRef(entryClass);
//    env->DeleteLocalRef(integerClass);
//
//    // 生成jstring字符串并返回
//    return env->NewStringUTF(buff);
//}

//jstring getCurrentTime() {
//    time_t t = time(nullptr);
//    char ch[64] = {0};
////    strftime(ch, sizeof(ch) - 1, "%Y-%m-%d %H:%M:%S", localtime(&t));
//    strftime(ch, sizeof(ch) - 1, "%Y-%m-%d %H", localtime(&t));
//    return (jstring)ch;
//}

